// 三个状态: PENDING,FULFILLED,REJECTED
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

// promise2: 新的promise对象
// x: 上一个then的返回值
// resolve: promise2的resolve
// reject: promise2的reject
const resolvePromise = (promise2, x, resolve, reject) => {
    // 自己等待自己是错误的实现
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise'))
    }
    let called //只能调用一次
    // x为函数或对象
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try {
            let then = x.then
            if (typeof then === 'function') {
                then.call(x, y => { // 根据 promise 的状态决定成功还是失败
                    if (called) return
                    called = true
                    // 递归解析过程(因为 promise 中还有 promise)
                    resolvePromise(promise2, y, resolve, reject)
                }, r => {
                    // 只要失败就失败
                    if (called) return
                    called = true
                    reject(r)
                })
            } else {
                // 如果x.then是普通值直接返回 resolve 作为结果
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    } else {
        // x如果x是普通值直接返回 resolve 作为结果
        resolve(x)
    }
}

class Promise {
    constructor (executor) {
        this.status = PENDING   // 默认状态为PENDING
        this.value = undefined  // 存放成功状态,默认undefined
        this.reason = undefined // 存放失败状态,默认undefined

        this.onResolvedCallbacks = [] // 存放成功回调
        this.onRejectesCallbacks = [] // 存放失败回调

        // 调用此方法就是成功
        let resolve = (value) => {
            // 如果 value 是一个 promise ,也要实现一个递归解析
            if (value instanceof Promise) {
                // 递归解析
                return value.then(resolve, reject)
            }
            // 状态为 PENDING 时才可以更新状态,防止 executor 中调用两次 resolve/reject 方法
            if (this.status === PENDING) {
                this.status = FULFILLED
                this.value = value
                this.onResolvedCallbacks.forEach(fn => fn())
            }
        }

        // 调用此方法就是失败
        let reject = (reason) => {
            // 状态为 PENDING 时才可以更新状态,防止 executor 中调用两次 resolve/reject 方法
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                this.onRejectesCallbacks.forEach(fn => fn())
            }
        }

        try {
            // 立即执行,将 resolve 和 reject 函数传给使用者
            executor(resolve, reject)
        } catch (e) {
            // 发生异常时执行失败逻辑
            reject(e)
        }
    }

    // 产生一个成功的 promise
    static resolve (data) {
        return new Promise((resolve, reject) => {
            resolve(data)
        })
    }

    // 产生一个失败的 promise
    static reject (reason) {
        return new Promise((resolve, reject) => {
            reject(reason)
        })
    }

    // 包含一个 then 方法,并接收两个参数 onFulfilled,onRejected
    then (onFulfilled, onRejected) {
        // 解决 onFulfilled,onRejected 没有传值问题
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
        // 因为错误的值要让后面访问到,所以这里要抛出个错误
        onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}

        // 每次调用 then 都返回一个新的 promise
        let promise2 = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value)
                        // x 可能是一个 promise
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === PENDING) {
                // 如果promise的状态是 pending，需要将 onFulfilled 和 onRejected 函数存放起来，等待状态确定后，再依次将对应的函数执行
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
                // 如果promise的状态是 pending，需要将 onFulfilled 和 onRejected 函数存放起来，等待状态确定后，再依次将对应的函数执行
                this.onRejectesCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
            }
        })
        return promise2
    }
}

// Promise.prototype.catch 用来捕获 promise 的异常，就相当于一个没有成功的 then
Promise.prototype.catch = function (errCallback) {
    return this.then(null, errCallback)
}

// Promise.prototype.finally 无论如何都会执行
Promise.prototype.finally = function (callback) {
    return this.then(value => {
        return Promise.resolve(callback()).then(() => value)
    }, reason => {
        return Promise.resolve(callback()).then(() => {throw reason})
    })
}

// promise.all 解决并发问题，多个异步并发获取最终的结果（如果有一个失败则失败）
Promise.all = function (values) {
    if (!Array.isArray(values)) {
        const type = typeof values
        return new TypeError(`TypeError: ${type} ${values} is not iterable`)
    }
    return new Promise((resolve, reject) => {
        let resultArr = []
        let orderIndex = 0
        const processResultByKey = (value, index) => {
            resultArr[index] = value
            if (++orderIndex === values.length) {
                resolve(resultArr)
            }
        }
        for (let i = 0; i < values.length; i++) {
            let value = values[i]
            if (value && typeof value.then === 'function') {
                value.then(value => {
                    processResultByKey(value, i)
                }, reject)
            } else {
                processResultByKey(value, i)
            }
        }
    })
}

// Promise.race 用来处理多个请求，采用最快的（谁先完成用谁的)
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        promises = Array.isArray(promises) ? promises : []
        // 一起执行
        for (let i = 0; i < promises.length; i++) {
            let val = promises[i]
            if (val && typeof val.then === 'function') {
                val.then(resolve, reject)
            } else {
                resolve(val)
            }
        }
    })
}

// Promise.allSettled 获取数组中每个 promise 的结果，无论成功或失败
Promise.allSettled = function (values) {
    if (!Array.isArray(values)) {
        const type = typeof values
        return new TypeError(`TypeError: ${type} ${values} is not iterable`)
    }
    return new Promise((resolve, reject) => {
        let resultArr = []
        let orderIndex = 0

        const processResultByKey = (value, index) => {
            resultArr[index] = value
            if (++orderIndex === values.length) {
                resolve(resultArr)
            }
        }
        for (let i = 0; i < values.length; i++) {
            let value = values[i]
            if (value && typeof value.then === 'function') {
                value.then(val => {
                    processResultByKey({ status: 'fulfilled', value: val }, i)
                }, err => {
                    processResultByKey({ status: 'rejected', value: err }, i)
                })
            } else {
                processResultByKey({ status: 'fulfilled', value: value }, i)
            }
        }
    })
}

// Promise.any 有一个是 fullfilled 则立即 resolve 出去，否则将所有 reject 结果收集起来并返回 AggregateError
Promise.any = function (promises) {
    return new Promise((resolve, reject) => {
        promises = Array.isArray(promises) ? promises : []
        let len = promises.length
        let errs = []
        if (len === 0) return reject(new AggregateError('All promises were rejected'))
        for (let i = 0; i < promises.length; i++) {
            let val = promises[i]
            if (val && typeof val.then === 'function') {
                val.then(val => {
                    resolve(val)
                }, err => {
                    len--
                    errs.push(err)
                    if (len === 0) return reject(new AggregateError(errs))
                })
            } else {
                resolve(val)
            }
        }
    })
}

// test
Promise.resolve(new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('ok')
    }, 1000)
})).then(data => {
    console.log(data, 'success')
    return new Promise((resolvePromise, reject) => {
        reject('err')
    })
}, err => {
    console.log('err1', err)
}).then(res => {
    console.log('then2', res)
}, err => {
    console.log('err2', err)
}).catch(err => {
    console.log(err, 'error')
})

// finally test
Promise.resolve(456).finally(() => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(123)
        }, 1000)
    })
}).then(data => {
    console.log(data, 'success')
}).catch(err => {
    console.log(err, 'error')
})

const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = new Promise((resolve, reject) => {
    setTimeout(reject, 1000, 'three')
})

Promise.allSettled([p1, p2, p3]).then(values => {
    console.log(values)
})

Promise.defer = Promise.deferred = function () {
    let dfd = {}
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}
module.exports = Promise
// Promises/A+规范 https://promisesaplus.cn/#point-46
// npm npm install promises-aplus-tests -g
// promises-aplus-tests ./Promise.js
